package cn.lkp.mall.product.service.impl;

import cn.lkp.common.constant.ProductConstant;
import cn.lkp.common.to.SkuHasStockVo;
import cn.lkp.common.to.SkuReductionTo;
import cn.lkp.common.to.SpuBoundTo;
import cn.lkp.common.to.es.SkuEsModel;
import cn.lkp.common.utils.R;
import cn.lkp.mall.product.entity.*;
import cn.lkp.mall.product.feign.CouponFeignService;
import cn.lkp.mall.product.feign.SearchFeignService;
import cn.lkp.mall.product.feign.WareFeignService;
import cn.lkp.mall.product.service.*;
import cn.lkp.mall.product.vo.*;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.lkp.common.utils.PageUtils;
import cn.lkp.common.utils.Query;

import cn.lkp.mall.product.dao.SpuInfoDao;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Slf4j
@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfoEntity> implements SpuInfoService {

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SpuInfoEntity> page = this.page(
                new Query<SpuInfoEntity>().getPage(params),
                new QueryWrapper<SpuInfoEntity>()
        );

        return new PageUtils(page);
    }
    @Autowired
    SpuInfoDescService spuInfoDescService;
    @Autowired
    SpuImagesService imagesService;
    @Autowired
    AttrService attrService;
    @Autowired
    ProductAttrValueService valueService;
    @Autowired
    SkuInfoService skuInfoService;
    @Autowired
    SkuImagesService skuImagesService;
    @Autowired
    SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    CouponFeignService couponFeignService;
    @Autowired
    BrandService brandService;
    @Autowired
    CategoryService categoryService;
    @Autowired
    WareFeignService wareFeignService;
    @Autowired
    SearchFeignService searchFeignService;
    @Autowired
    AmqpTemplate amqpTemplate;
    @Transactional
    @Override
    //TODO 完善分布式事务
    public void saveSpuInfo(SpuSaveVo vo) {
        //1.保存spu基本信息pms_spu_info
        SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
        BeanUtils.copyProperties(vo,spuInfoEntity);
        //infoEntity中的字段createTime和updateTime在vo中没有，直接赋初始值
        spuInfoEntity.setCreateTime(new Date());
        spuInfoEntity.setUpdateTime(new Date());
        this.saveBaseSpuInfo(spuInfoEntity);
        //2.保存spu的描述信息pms_spu_info_desc
        List<String> decript = vo.getDecript();
        SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
        descEntity.setSpuId(spuInfoEntity.getId());
        descEntity.setDecript(String.join(",",decript));
        spuInfoDescService.saveSpuInfoDesc(descEntity);
        //3.保存spu的图片集pms_spu_images
        List<String> images = vo.getImages();
        imagesService.saveImages(spuInfoEntity.getId(),images);
        //4.保存spu的规格参数 pms_product_attr_value
        List<BaseAttrs> baseAttrsList = vo.getBaseAttrs();
        List<ProductAttrValueEntity> collect = baseAttrsList.stream().map((attr) -> {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setSpuId(spuInfoEntity.getId());
            valueEntity.setAttrId(attr.getAttrId());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            AttrEntity attrEntity = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(attrEntity.getAttrName());
            return valueEntity;
        }).collect(Collectors.toList());
        valueService.saveProductAttr(collect);
        //5.保存spu的积分信息 easyshopping_sms-》sms_spu_bounds
        Bounds bounds = vo.getBounds();
        SpuBoundTo spuBoundTo = new SpuBoundTo();
        BeanUtils.copyProperties(bounds,spuBoundTo);
        spuBoundTo.setSpuId(spuInfoEntity.getId());
        R r_spu_bound = couponFeignService.saveSpuBound(spuBoundTo);
        if(r_spu_bound.getCode()!=0){
            log.error("远程保存spu积分信息失败");
        }
        //6.保存当前spu对应的sku信息
        List<Skus> skus = vo.getSkus();
        /**
         * private String skuName;
         *     private BigDecimal price;
         *     private String skuTitle;
         *     private String skuSubtitle;
         */
        if(skus != null && skus.size()>0){
            skus.forEach(item->{
                //保存spuInfo后拿到spu_id，才能保存图片，先设置上默认图片
                String defaultImg = "";
                for(Images img:item.getImages()){
                    if(img.getDefaultImg()==1){
                        defaultImg = img.getImgUrl();
                    }
                }
                //6.1 保存sku的基本信息pms_sku_info，拿到sku_id,用于在其他表中保存数据。
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
                BeanUtils.copyProperties(item,skuInfoEntity);
                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);
                skuInfoEntity.setSpuId(spuInfoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
                skuInfoService.saveSkuInfo(skuInfoEntity);
                //拿到skuId
                Long skuId = skuInfoEntity.getSkuId();
                //6.2 保存sku的图片集pms_sku_images
                List<SkuImagesEntity> skuImagesEntityList = item.getImages().stream().map((img) -> {
                    SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                    skuImagesEntity.setSkuId(skuId);
                    skuImagesEntity.setImgUrl(img.getImgUrl());
                    skuImagesEntity.setDefaultImg(img.getDefaultImg());
                    return skuImagesEntity;
                }).filter(entity->{
                    //没有地址的图片将会被过滤
                    return !StringUtils.isEmpty(entity.getImgUrl());
                }).collect(Collectors.toList());
                skuImagesService.saveBatch(skuImagesEntityList);
                //6.3 保存sku的销售属性pms_sku_sale_attr_value
                List<Attr> attrList = item.getAttr();
                List<SkuSaleAttrValueEntity> skuSaleAttrValueEntityList = attrList.stream().map((attr) -> {
                    SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
                    skuSaleAttrValueEntity.setSkuId(skuId);
                    BeanUtils.copyProperties(attr,skuSaleAttrValueEntity);
                    return skuSaleAttrValueEntity;
                }).collect(Collectors.toList());
                //6.4 保存sku的打折、满减、会员优惠信息,这里需要跨库，easyshopping_sms-》sms_sku_ladder   sms_sku_full_reduction   sms_member_price
                skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntityList);
                SkuReductionTo skuReductionTo = new SkuReductionTo();
                BeanUtils.copyProperties(item,skuReductionTo);
                skuReductionTo.setSkuId(skuId);
                if(skuReductionTo.getFullCount()>0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0"))==1){
                    R r_sku_reduction = couponFeignService.saveSkuReduction(skuReductionTo);
                    if(r_sku_reduction.getCode()!=0){
                        log.error("远程保存spu打折、满减、会员优惠信息失败");
                    }
                }
            });
        }
    }

    @Override
    public void saveBaseSpuInfo(SpuInfoEntity infoEntity) {
        this.baseMapper.insert(infoEntity);
    }

    @Override
    public PageUtils queryPageByCondition(Map<String, Object> params) {
        QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        if(!StringUtils.isEmpty(key)){
            wrapper.and((w)->{
                w.eq("id",key).or().like("spu_name",key);
            });
        }
        String status = (String) params.get("status");
        if(!StringUtils.isEmpty(status)){
            wrapper.eq("publish_status",status);
        }
        String catelogId = (String) params.get("catelogId");
        if(!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)){
            wrapper.eq("catalog_id",catelogId);
        }
        String brandId = (String) params.get("brandId");
        if(!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)){
            wrapper.eq("brand_id",brandId);
        }

        IPage<SpuInfoEntity> page = this.page(
                new Query<SpuInfoEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void up(Long spuId) {
        //1.查出当前spuId对应的所有sku信息，包含品牌名字
        List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);
        //简写方式(map中手动敲入类型的首字母，大写S，会有提示)，返回skuId的集合，作为wareFeignService.getSkuHasStock（）的参数
        List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
        /**
         外部查询检索属性列表attrsList
         */
        //（1）查找当前spuId对应的所有基本属性
        List<ProductAttrValueEntity> baseAttrs = valueService.baseAttrlistforspu(spuId);
        //（2）收集这些属性的id
        List<Long> attrIds = baseAttrs.stream().map(attr -> {
            return attr.getAttrId();
        }).collect(Collectors.toList());
        //（3）根据这些id到attr表中查找id在其范围内且检索类型为可检索的id
        List<Long> searchAttrIds = attrService.selectSearchAttrIds(attrIds);
        Set<Long> idSet = new HashSet<>(searchAttrIds);
        //（4）从baseAttrs中过滤出属于searchAttrIds范围内的属性,并映射成属性列表
        List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {
            return idSet.contains(item.getAttrId());
        }).map(item -> {
            SkuEsModel.Attrs attrs = new SkuEsModel.Attrs();
            BeanUtils.copyProperties(item, attrs);
            return attrs;
        }).collect(Collectors.toList());

        //调用库存服务,查询是否有库存
        //stockMap初始值为null
        Map<Long, Boolean> stockMap = null;
        try{
            //如果在内部每次调用远程服务，需要调用多次，非常慢。因此考虑在商品模块建一个接口，在外部统一查询所有sku有无库存
            // hasStock
            R r = wareFeignService.getSkuHasStock(skuIdList);
            TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {
            };
            //将list转成map,使用Collectors.toMap()，传入两个参数,第一个参数是返回的skuId,第二个参数是简写形式，返回库存状态，可以省略小括号，大括号和return
            stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));
        }catch (Exception e){
            //{}为占位符
            log.error("调用库存服务异常：原因{}",e);
        }
        //2.封装每个sku的信息，将数据组装成SkuEsModel类型
        Map<Long, Boolean> finalStockMap = stockMap;
        List<SkuEsModel> upProducts = skus.stream().map(sku -> {
            SkuEsModel skuEsModel = new SkuEsModel();
            //属性赋值
            //1）.属性对拷
            BeanUtils.copyProperties(sku, skuEsModel);
            //2）.手动赋值skuPrice skuImg 赋默认值hotScore
            skuEsModel.setSkuPrice(sku.getPrice());
            skuEsModel.setSkuImg(sku.getSkuDefaultImg());
            skuEsModel.setHotScore(0L);
            //3). 设置库存信息，在内部传入库存状态
            if(finalStockMap == null){
                //若远程调用失败，则stockMap一直为空，默认设置有库存
                skuEsModel.setHasStock(true);
            }else{
                skuEsModel.setHasStock(finalStockMap.get(sku.getSkuId()));
            }
            /**
             * 4）. 赋值下列字段
             * private String brandName;
             *     private String brandImg;
             *     private String catalogName;
             */
            BrandEntity brandEntity = brandService.getById(skuEsModel.getBrandId());
            skuEsModel.setBrandName(brandEntity.getName());
            skuEsModel.setBrandImg(brandEntity.getLogo());
            CategoryEntity categoryEntity = categoryService.getById(skuEsModel.getCatalogId());
            skuEsModel.setCatalogName(categoryEntity.getName());
            /**
             * 5）.查出当前sku对应的所有可以用来被检索的规格属性，由于一个spu对应多个sku,所以如果在里面
             * 查，需要查多遍，因此放到外面，只需查一遍。
             * 在内部设置检索属性，传入外部查询到的检索属性列表attrsList
             * private List<Attrs> attrs;
             *     //方法公开以便第三方工具对其序列化和反序列化
             *     @Data
             *     public static class Attrs{
             *         private Long attrId;
             *         private String attrName;
             *         private String attrValue;
             *
             */
            skuEsModel.setAttrs(attrsList);
            return skuEsModel;
        }).collect(Collectors.toList());
        //TODO 3.将数据发送给mall-search保存到es中
        R r = searchFeignService.productStatusUp(upProducts);
        if(r.getCode() == 0){
            //远程调用成功
            //修改当前spu的发布状态
            this.baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.UP_SPU.getCode());
        }else{
            //远程调用失败
            //TODO 重复调用？接口幂等性？重试机制？
        }
    }

    @Override
    public SpuInfoEntity getSpuInfoBySkuId(Long skuId) {
        //先查询sku表里的数据
        SkuInfoEntity skuInfoEntity = skuInfoService.getById(skuId);
        //获得spuId
        Long spuId = skuInfoEntity.getSpuId();
        //再通过spuId查询spuInfo信息表里的数据
        SpuInfoEntity spuInfoEntity = this.baseMapper.selectById(spuId);
        //查询品牌表的数据获取品牌名
        BrandEntity brandEntity = brandService.getById(spuInfoEntity.getBrandId());
        spuInfoEntity.setBrandName(brandEntity.getName());
        return spuInfoEntity;
    }

}